home *** CD-ROM | disk | FTP | other *** search
/ Software of the Month Club 1996 April / Software of the Month Club 1996 April.iso / pc / os2 / psutils / src / psnup.c < prev    next >
C/C++ Source or Header  |  1996-02-21  |  10KB  |  337 lines

  1. /* psnup.c
  2.  * Copyright (C) Angus J. C. Duggan 1991-1995
  3.  * See file LICENSE for details.
  4.  *
  5.  * put multiple pages onto one physical sheet of paper
  6.  *
  7.  * Usage:
  8.  *      psnup [-q] [-w<dim>] [-h<dim>] [-ppaper] [-b<dim>] [-m<dim>]
  9.  *            [-l] [-c] [-f] [-sscale] [-d<wid>] [-nup] [in [out]]
  10.  *              -w<dim> sets the paper width
  11.  *              -h<dim> sets the paper height
  12.  *              -ppaper sets the paper size (width and height) by name
  13.  *              -W<dim> sets the input paper width, if different from output
  14.  *              -H<dim> sets the input paper height, if different from output
  15.  *              -Ppaper sets the input paper size, if different from output
  16.  *              -m<dim> sets the margin around the paper
  17.  *              -b<dim> sets the border around each page
  18.  *              -sscale alters the scale at which the pages are displayed
  19.  *              -l      used if pages are in landscape orientation (rot left)
  20.  *              -r      used if pages are in seascape orientation (rot right)
  21.  *         -c    for column-major layout
  22.  *        -f    for flipped (wider than tall) pages
  23.  *         -d<wid>    to draw the page boundaries
  24.  */
  25.  
  26. #include "psutil.h"
  27. #include "psspec.h"
  28. #include "pserror.h"
  29. #include "patchlev.h"
  30.  
  31. char *program ;
  32. int pages ;
  33. int verbose ;
  34. FILE *infile ;
  35. FILE *outfile ;
  36. char pagelabel[BUFSIZ] ;
  37. int pageno ;
  38.  
  39. static void usage(void)
  40. {
  41.    fprintf(stderr, "%s release %d patchlevel %d\n", program, RELEASE, PATCHLEVEL);
  42.    fprintf(stderr, "Copyright (C) Angus J. C. Duggan, 1991-1995. See file LICENSE for details.\n");
  43.    fprintf(stderr, "Usage: %s [-q] [-wwidth] [-hheight] [-ppaper] [-Wwidth] [-Hheight] [-Ppaper] [-l] [-r] [-c] [-f] [-mmargin] [-bborder] [-dlwidth] [-sscale] [-nup] [infile [outfile]]\n",
  44.        program);
  45.    fflush(stderr);
  46.    exit(1);
  47. }
  48.  
  49. static void argerror(void)
  50. {
  51.    message(FATAL, "bad dimension\n");
  52. }
  53.  
  54. #define MIN(x,y) ((x) > (y) ? (y) : (x))
  55. #define MAX(x,y) ((x) > (y) ? (x) : (y))
  56.  
  57. /* return next larger exact divisor of number, or 0 if none. There is probably
  58.  * a much more efficient method of doing this, but the numbers involved are
  59.  * small, so it's not a big loss. */
  60. static int nextdiv(int n, int m)
  61. {
  62.    while (++n <= m) {
  63.       if (m%n == 0)
  64.      return (n);
  65.    }
  66.    return (0);
  67. }
  68.  
  69. void main(int argc, char *argv[])
  70. {
  71.    int horiz, vert, rotate, column, flip, leftright, topbottom;
  72.    int nup = 1;
  73.    double draw = 0;                /* draw page borders */
  74.    double scale;                /* page scale */
  75.    double uscale = 0;                /* user supplied scale */
  76.    double ppwid, pphgt;                /* paper dimensions */
  77.    double margin, border;            /* paper & page margins */
  78.    double vshift, hshift;            /* page centring shifts */
  79.    double iwidth, iheight ;            /* input paper size */
  80.    double tolerance = 100000;            /* layout tolerance */
  81.    Paper *paper;
  82.  
  83. #ifdef PAPER
  84.    if ( (paper = findpaper(PAPER)) != (Paper *)0 ) {
  85.       width = (double)PaperWidth(paper);
  86.       height = (double)PaperHeight(paper);
  87.    }
  88. #endif
  89.  
  90.    margin = border = vshift = hshift = column = flip = 0;
  91.    leftright = topbottom = 1;
  92.    iwidth = iheight = -1 ;
  93.  
  94.    infile = stdin;
  95.    outfile = stdout;
  96.    verbose = 1;
  97.    for (program = *argv++; --argc; argv++) {
  98.       if (argv[0][0] == '-') {
  99.      switch (argv[0][1]) {
  100.      case 'q':    /* quiet */
  101.         verbose = 0;
  102.         break;
  103.      case 'd':    /* draw borders */
  104.         if (argv[0][2])
  105.            draw = singledimen(*argv+2, argerror, usage);
  106.         else
  107.            draw = 1;
  108.         break;
  109.      case 'l':    /* landscape (rotated left) */
  110.         column = !column;
  111.         topbottom = !topbottom;
  112.         break;
  113.      case 'r':    /* seascape (rotated right) */
  114.         column = !column;
  115.         leftright = !leftright;
  116.         break;
  117.      case 'f':    /* flipped */
  118.         flip = 1;
  119.         break;
  120.      case 'c':    /* column major layout */
  121.         column = !column;
  122.         break;
  123.      case 'w':    /* page width */
  124.         width = singledimen(*argv+2, argerror, usage);
  125.         break;
  126.      case 'W':    /* input page width */
  127.         iwidth = singledimen(*argv+2, argerror, usage);
  128.         break;
  129.      case 'h':    /* page height */
  130.         height = singledimen(*argv+2, argerror, usage);
  131.         break;
  132.      case 'H':    /* input page height */
  133.         iheight = singledimen(*argv+2, argerror, usage);
  134.         break;
  135.      case 'm':    /* margins around whole page */
  136.         margin = singledimen(*argv+2, argerror, usage);
  137.         break;
  138.      case 'b':    /* border around individual pages */
  139.         border = singledimen(*argv+2, argerror, usage);
  140.         break;
  141.      case 't':    /* layout tolerance */
  142.         tolerance = atof(*argv+2);
  143.         break;
  144.      case 's':    /* override scale */
  145.         uscale = atof(*argv+2);
  146.         break;
  147.      case 'p':    /* output (and by default input) paper type */
  148.         if ( (paper = findpaper(*argv+2)) != (Paper *)0 ) {
  149.            width = (double)PaperWidth(paper);
  150.            height = (double)PaperHeight(paper);
  151.         } else
  152.            message(FATAL, "paper size '%s' not recognised\n", *argv+2);
  153.         break;
  154.      case 'P':    /* paper type */
  155.         if ( (paper = findpaper(*argv+2)) != (Paper *)0 ) {
  156.            iwidth = (double)PaperWidth(paper);
  157.            iheight = (double)PaperHeight(paper);
  158.         } else
  159.            message(FATAL, "paper size '%s' not recognised\n", *argv+2);
  160.         break;
  161.      case 'n':    /* n-up, for compatibility with other psnups */
  162.         if (argc >= 2) {
  163.            argv++;
  164.            argc--;
  165.            if ((nup = atoi(*argv)) < 1)
  166.           message(FATAL, "-n %d too small\n", nup);
  167.         } else
  168.            message(FATAL, "argument expected for -n\n");
  169.         break;
  170.      case '1':
  171.      case '2':
  172.      case '3':
  173.      case '4':
  174.      case '5':
  175.      case '6':
  176.      case '7':
  177.      case '8':
  178.      case '9':
  179.         nup = atoi(*argv+1);
  180.         break;
  181.      case 'v':    /* version */
  182.      default:
  183.         usage();
  184.      }
  185.       } else if (infile == stdin) {
  186.      if ((infile = fopen(*argv, OPEN_READ)) == NULL)
  187.         message(FATAL, "can't open input file %s\n", *argv);
  188.       } else if (outfile == stdout) {
  189.      if ((outfile = fopen(*argv, OPEN_WRITE)) == NULL)
  190.         message(FATAL, "can't open output file %s\n", *argv);
  191.       } else usage();
  192.    }
  193. #if defined(MSDOS) || defined(WINNT)
  194.    if ( infile == stdin ) {
  195.       int fd = fileno(stdin) ;
  196.       if ( setmode(fd, O_BINARY) < 0 )
  197.          message(FATAL, "can't open input file %s\n", argv[4]);
  198.     }
  199.    if ( outfile == stdout ) {
  200.       int fd = fileno(stdout) ;
  201.       if ( setmode(fd, O_BINARY) < 0 )
  202.          message(FATAL, "can't reset stdout to binary mode\n");
  203.     }
  204. #endif
  205.    if ((infile=seekable(infile))==NULL)
  206.       message(FATAL, "can't seek input\n");
  207.  
  208.    if (width <= 0 || height <= 0)
  209.       message(FATAL, "page width and height must be set\n");
  210.  
  211.    /* subtract paper margins from height & width */
  212.    ppwid = width - margin*2;
  213.    pphgt = height - margin*2;
  214.  
  215.    if (ppwid <= 0 || pphgt <= 0)
  216.       message(FATAL, "paper margins are too large\n");
  217.  
  218.    /* set default values of input height & width */
  219.    if ( iwidth > 0 )
  220.      width = iwidth ;
  221.    if ( iheight > 0 )
  222.      height = iheight ;
  223.  
  224.    /* Finding the best layout is an optimisation problem. We try all of the
  225.     * combinations of width*height in both normal and rotated form, and
  226.     * minimise the wasted space. */
  227.    {
  228.       double best = tolerance;
  229.       int hor;
  230.       for (hor = 1; hor; hor = nextdiv(hor, nup)) {
  231.      int ver = nup/hor;
  232.      /* try normal orientation first */
  233.      double scl = MIN(pphgt/(height*ver), ppwid/(width*hor));
  234.      double optim = (ppwid-scl*width*hor)*(ppwid-scl*width*hor) +
  235.         (pphgt-scl*height*ver)*(pphgt-scl*height*ver);
  236.      if (optim < best) {
  237.         best = optim;
  238.         /* recalculate scale to allow for internal borders */
  239.         scale = MIN((pphgt-2*border*ver)/(height*ver),
  240.             (ppwid-2*border*hor)/(width*hor));
  241.         hshift = (ppwid/hor - width*scale)/2;
  242.         vshift = (pphgt/ver - height*scale)/2;
  243.         horiz = hor; vert = ver;
  244.         rotate = flip;
  245.      }
  246.      /* try rotated orientation */
  247.      scl = MIN(pphgt/(width*hor), ppwid/(height*ver));
  248.      optim = (pphgt-scl*width*hor)*(pphgt-scl*width*hor) +
  249.         (ppwid-scl*height*ver)*(ppwid-scl*height*ver);
  250.      if (optim < best) {
  251.         best = optim;
  252.         /* recalculate scale to allow for internal borders */
  253.         scale = MIN((pphgt-2*border*hor)/(width*hor),
  254.             (ppwid-2*border*ver)/(height*ver));
  255.         hshift = (ppwid/ver - height*scale)/2;
  256.         vshift = (pphgt/hor - width*scale)/2;
  257.         horiz = ver; vert = hor;
  258.         rotate = !flip;
  259.      }
  260.       }
  261.  
  262.       /* fail if nothing better than worst tolerance was found */
  263.       if (best == tolerance)
  264.      message(FATAL, "can't find acceptable layout for %d-up\n", nup);
  265.    }
  266.  
  267.    if (flip) {    /* swap width & height for clipping */
  268.       double tmp = width;
  269.       width = height;
  270.       height = tmp;
  271.    }
  272.  
  273.    if (rotate) {    /* rotate leftright and topbottom orders */
  274.       int tmp = topbottom;
  275.       topbottom = !leftright;
  276.       leftright = tmp;
  277.       column = !column;
  278.    }
  279.  
  280.    /* now construct specification list and run page rearrangement procedure */
  281.    {
  282.       int page = 0;
  283.       PageSpec *specs, *tail;
  284.  
  285.       tail = specs = newspec();
  286.  
  287.       while (page < nup) {
  288.      int up, across;        /* page index */
  289.  
  290.      if (column) {
  291.         if (leftright)        /* left to right */
  292.            across = page/vert;
  293.         else            /* right to left */
  294.            across = horiz-1-page/vert;
  295.         if (topbottom)        /* top to bottom */
  296.            up = vert-1-page%vert;
  297.         else            /* bottom to top */
  298.            up = page%vert;
  299.      } else {
  300.         if (leftright)        /* left to right */
  301.            across = page%horiz;
  302.         else            /* right to left */
  303.            across = horiz-1-page%horiz;
  304.         if (topbottom)        /* top to bottom */
  305.            up = vert-1-page/horiz;
  306.         else            /* bottom to top */
  307.            up = page/horiz;
  308.      }
  309.      if (rotate) {
  310.         tail->xoff = margin + (across+1)*ppwid/horiz - hshift;
  311.         tail->rotate = 90;
  312.         tail->flags |= ROTATE;
  313.      } else {
  314.         tail->xoff = margin + across*ppwid/horiz + hshift;
  315.      }
  316.      tail->pageno = page;
  317.      if (uscale > 0)
  318.         tail->scale = uscale;
  319.      else
  320.         tail->scale = scale;
  321.      tail->flags |= SCALE;
  322.      tail->yoff = margin + up*pphgt/vert + vshift;
  323.      tail->flags |= OFFSET;
  324.      if (++page < nup) {
  325.         tail->flags |= ADD_NEXT;
  326.         tail->next = newspec();
  327.         tail = tail->next;
  328.      }
  329.       }
  330.       
  331.       pstops(nup, 1, 0, specs, draw);        /* do page rearrangement */
  332.    }
  333.  
  334.    exit(0);
  335. }
  336.  
  337.